﻿using System;
using System.Collections;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

namespace XUHttpDownload
{
    public class XUHttpSocket : XUHttpSocketBase
    {
        private XUHttpSocketRequest request;

        private bool flagRunner;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public XUHttpSocket(string server, int port, int recvBuffSize, int fileBuffSize) : base(server, port)
        {
            request = new XUHttpSocketRequest(recvBuffSize, fileBuffSize);
            request.Reset();

            flagRunner = true;
            new Thread(new ThreadStart(ThreadRunner)).Start();
        }

        public void Close()
        {
            flagRunner = false;
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private void ThreadRunner()
        {
            Socket socket = null;

            long lastMilliseconds = 0;
            long timeoutMilliseconds = 8000;

            Debug.Log("XUHttpSocket thread start, flagRunner = " + flagRunner);

            while (flagRunner)
            {
                Thread.Sleep(20);
                try
                {
                    lock (request)
                    {
                        if (request.status == XUHttpSocketRequest.Status.None || request.status == XUHttpSocketRequest.Status.Response)
                        {
                            continue;
                        }

                        if (request.status == XUHttpSocketRequest.Status.Cancel)
                        {
                            Debug.Log("XUHttpSocket request cancel \n" + request.requestHeader);
                            DisconnectSocket(socket);
                            request.Reset();
                            continue;
                        }

                        if (socket == null || !socket.Connected)
                        {
                            socket = ConnectSocket(server, port);
                            Debug.Log("XUHttpSocket connect " + server + ":" + port);
                        }
                        else
                        {
                            //发送请求
                            if (request.status == XUHttpSocketRequest.Status.Ready)
                            {
                                if (SocketSend(socket, request.requestHeader))
                                {
                                    request.status = XUHttpSocketRequest.Status.Request;
                                    lastMilliseconds = GetMilliseconds();
                                }
                            }

                            //接收数据
                            if (request.status == XUHttpSocketRequest.Status.Request)
                            {
                                if (socket.Poll(1, SelectMode.SelectRead))
                                {
                                    if (socket.Available > 0)
                                    {
                                        SocketReceive(socket, request);
                                        lastMilliseconds = GetMilliseconds();
                                    }
                                    else
                                    {
                                        if ((GetMilliseconds() - lastMilliseconds) > timeoutMilliseconds)
                                        {
                                            Debug.LogError("XUHttpSocket TimeOut by Available is " + socket.Available);
                                            CancelDownload();
                                        }
                                    }
                                }
                                else
                                {
                                    if ((GetMilliseconds() - lastMilliseconds) > timeoutMilliseconds)
                                    {
                                        Debug.LogError("XUHttpSocket TimeOut by Poll is false");
                                        CancelDownload();
                                    }
                                }
                            }
                        }
                    }
                }
                catch (SocketException ex)
                {
                    Debug.LogError("XUHttpSocket " + ex.ErrorCode + " : " + ex);
                    CancelDownload();
                }
                catch (Exception ex)
                {
                    Debug.LogError("XUHttpSocket " + ex);
                    CancelDownload();
                }
            }

            DisconnectSocket(socket);

            Debug.Log("XUHttpSocket thread stop, flagRunner = " + flagRunner);
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// 请求下载
        /// </summary>
        public IEnumerator RequestDownload(string url, Action<string, object, int, byte[]> callSuccess, Action<string, object> callFail, Action<string, object, float> callProgress, object param = null)
        {
            if (request.status == XUHttpSocketRequest.Status.None || request.status == XUHttpSocketRequest.Status.Response)
            {
                request.Reset();
                request.requestHeader = GetRequest(url);
                request.status = XUHttpSocketRequest.Status.Ready;

                while (request.status == XUHttpSocketRequest.Status.Ready || request.status == XUHttpSocketRequest.Status.Request)
                {
                    if (request.fileLength > 0)
                    {
                        callProgress?.Invoke(url, param, request.fileOffset * 1f / request.fileLength);
                    }
                    yield return new WaitForSeconds(0.1f);
                }

                if (request.status == XUHttpSocketRequest.Status.Response)
                {
                    callProgress?.Invoke(url, param, request.fileOffset * 1f / request.fileLength);
                    callSuccess?.Invoke(url, param, request.fileLength, request.fileBuffer);
                }
                else
                {
                    callFail?.Invoke(url, param);
                }
            }
            else
            {
                Debug.LogError("XUHttpSocket is busy, need to wait for the current task to complete, unable to download\n" + url);
                callFail?.Invoke(url, param);
            }
        }

        /// <summary>
        /// 取消下载
        /// </summary>
        public void CancelDownload()
        {
            request.status = XUHttpSocketRequest.Status.Cancel;
        }
    }
}